package com.flyco.pageindicator.indicator;

import com.flyco.pageindicator.anim.base.IndicatorBaseAnimator;
import com.flyco.pageindicator.indicator.base.PageIndicator;
import com.flyco.pageindicator.util.AttrUtil;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Image;
import ohos.agp.components.PageSlider;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.app.Context;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 页面指示器
 */
public class FlycoPageIndicator extends DirectionalLayout implements PageIndicator {

    private static final String TAG = FlycoPageIndicator.class.getName();

    private final String FlycoPageIndicaor_fpi_width = "fpi_width";
    private final String FlycoPageIndicaor_fpi_height = "fpi_height";
    private final String FlycoPageIndicaor_fpi_gap = "fpi_gap";
    private final String FlycoPageIndicaor_fpi_strokeWidth = "fpi_strokeWidth";
    private final String FlycoPageIndicaor_fpi_strokeColor = "fpi_strokeColor";
    private final String FlycoPageIndicaor_fpi_isSnap = "fpi_isSnap";
    private final String FlycoPageIndicaor_fpi_selectColor = "fpi_selectColor";
    private final String FlycoPageIndicaor_fpi_unselectColor = "fpi_unselectColor";
    private final String FlycoPageIndicaor_fpi_cornerRadius = "fpi_cornerRadius";
    private final String FlycoPageIndicaor_fpi_selectRes = "fpi_selectRes";
    private final String FlycoPageIndicaor_fpi_unselectRes = "fpi_unselectRes";

    private Context context;
    private PageSlider pageSlider;
    private DependentLayout rl_parent;
    private Image selectView;
    private ArrayList<Image> indicatorViews = new ArrayList<>();
    private int count;

    private int currentItem;
    private int lastItem;
    private int indicatorWidth;
    private int indicatorHeight;
    private int indicatorGap;
    private int cornerRadius;
    private Element selectDrawable;
    private Element unSelectDrawable;
    private int strokeWidth;
    private int strokeColor;
    private boolean isSnap; // 是否显示过度位移动画
    private int positionOffset1;
    private int positionOffset2;

    private Class<? extends IndicatorBaseAnimator> selectAnimClass;
    private Class<? extends IndicatorBaseAnimator> unselectAnimClass;

    public FlycoPageIndicator(Context context) {
        this(context, null);
    }

    public FlycoPageIndicator(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public FlycoPageIndicator(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        this.context = context;
        rl_parent = new DependentLayout(context);
        addComponent(rl_parent, new LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT));
        AttrUtil attrUtil = new AttrUtil(attrSet);
        indicatorWidth = attrUtil.getDimensionValue(FlycoPageIndicaor_fpi_width, vp2px(16));
        indicatorHeight = attrUtil.getDimensionValue(FlycoPageIndicaor_fpi_height, vp2px(16));
        indicatorGap = attrUtil.getDimensionValue(FlycoPageIndicaor_fpi_gap, vp2px(20));
        cornerRadius = attrUtil.getDimensionValue(FlycoPageIndicaor_fpi_cornerRadius, vp2px(8));
        strokeWidth = attrUtil.getDimensionValue(FlycoPageIndicaor_fpi_strokeWidth, vp2px(0));
        strokeColor = attrUtil.getColorValue(FlycoPageIndicaor_fpi_strokeColor, 0xffffffff);
        isSnap = attrUtil.getBooleanValue(FlycoPageIndicaor_fpi_isSnap, false);
        int selectColor = attrUtil.getColorValue(FlycoPageIndicaor_fpi_selectColor, 0xffffffff);
        int unselectColor = attrUtil.getColorValue(FlycoPageIndicaor_fpi_unselectColor, 0xffffff88);
        selectDrawable = attrUtil.getElement(FlycoPageIndicaor_fpi_selectRes, getDrawable(selectColor, cornerRadius));
        unSelectDrawable = attrUtil.getElement(FlycoPageIndicaor_fpi_unselectRes, getDrawable(unselectColor, cornerRadius));
    }

    /**
     * call before setPageSlider. set indicator width, unit vp, default 16vp
     *
     * @param indicatorWidth indicator width
     * @return flycoPageIndicator
     */
    public FlycoPageIndicator setIndicatorWidth(float indicatorWidth) {
        this.indicatorWidth = vp2px(indicatorWidth);
        return this;
    }

    /**
     * call before setPageSlider. set indicator height, unit vp, default 16vp
     *
     * @param indicatorHeight indicator height
     * @return flycoPageIndicator
     */
    public FlycoPageIndicator setIndicatorHeight(float indicatorHeight) {
        this.indicatorHeight = vp2px(indicatorHeight);
        return this;
    }

    /**
     * call before setPageSlider. set gap between two indicators, unit vp, default 20vp
     *
     * @param indicatorGap gap between two indicators
     * @return flycoPageIndicator
     */
    public FlycoPageIndicator setIndicatorGap(float indicatorGap) {
        this.indicatorGap = vp2px(indicatorGap);
        return this;
    }

    /**
     * call before setPageSlider. set indicator select color, default "#ffffffff" "#ffffff88"
     *
     * @param selectColor   indicator select color
     * @param unselectColor indicator unselect color
     * @return flycoPageIndicator
     */
    public FlycoPageIndicator setIndicatorSelectColor(int selectColor, int unselectColor) {
        this.selectDrawable = getDrawable(selectColor, cornerRadius);
        this.unSelectDrawable = getDrawable(unselectColor, cornerRadius);
        return this;
    }

    /**
     * call before setPageSlider. set indicator corner raduis, unit vp, default 8vp
     *
     * @param cornerRadius indicator corner raduis
     * @return flycoPageIndicator
     */
    public FlycoPageIndicator setCornerRadius(float cornerRadius) {
        this.cornerRadius = vp2px(cornerRadius);
        return this;
    }

    /**
     * call before setPageSlider. set width of the stroke used to draw the indicators, unit dp, default 0vp
     *
     * @param strokeWidth width of the stroke used to draw the indicators
     * @return flycoPageIndicator
     */
    public FlycoPageIndicator setStrokeWidth(int strokeWidth) {
        this.strokeWidth = strokeWidth;
        return this;
    }

    /**
     * call before setPageSlider. set color of the stroke used to draw the indicators. default "#ffffffff"
     *
     * @param strokeColor color of the stroke used to draw the indicators
     * @return flycoPageIndicator
     */
    public FlycoPageIndicator setStrokeColor(int strokeColor) {
        this.strokeColor = strokeColor;
        return this;
    }

    /**
     * call before setPageSlider. Whether or not the selected indicator snaps to the indicators. default false
     *
     * @param isSnap Whether or not the selected indicator snaps to the indicators
     * @return flycoPageIndicator
     */
    public FlycoPageIndicator setIsSnap(boolean isSnap) {
        this.isSnap = isSnap;
        return this;
    }

    /**
     * call before setPageSlider. set indicator select anim. only valid when isSnap is true
     *
     * @param selectAnimClass indicator select anim
     * @return flycoPageIndicator
     */
    public FlycoPageIndicator setSelectAnimClass(Class<? extends IndicatorBaseAnimator> selectAnimClass) {
        this.selectAnimClass = selectAnimClass;
        return this;
    }

    /**
     * call before setPageSlider. set indicator unselect anim. only valid when isSnap is true
     *
     * @param unselectAnimClass indicator unselect anim
     * @return flycoPageIndicator
     */
    public FlycoPageIndicator setUnselectAnimClass(Class<? extends IndicatorBaseAnimator> unselectAnimClass) {
        this.unselectAnimClass = unselectAnimClass;
        return this;
    }

    public int getCurrentItem() {
        return currentItem;
    }

    public int getIndicatorWidth() {
        return indicatorWidth;
    }

    public int getIndicatorHeight() {
        return indicatorHeight;
    }

    public int getIndicatorGap() {
        return indicatorGap;
    }

    public int getCornerRadius() {
        return cornerRadius;
    }

    public int getStrokeWidth() {
        return strokeWidth;
    }

    public int getStrokeColor() {
        return strokeColor;
    }

    public boolean isSnap() {
        return isSnap;
    }

    @Override
    public void setCurrentItem(int item) {
        if (isValid()) {
            pageSlider.setCurrentPage(item);
        }
    }

    @Override
    public void setPageSlider(PageSlider pageSlider) {
        if (isValid()) {
            setPageSlider(pageSlider, pageSlider.getProvider().getCount());
        }
    }

    @Override
    public void setPageSlider(PageSlider pageSlider, int realCount) {
        this.pageSlider = pageSlider;
        if (isValid()) {
            this.count = realCount;
            pageSlider.addPageChangedListener(this);
            createIndicators();
        }
    }

    @Override
    public void onPageSliding(int position, float positionOffset, int positionOffsetPixels) {
        if (!isSnap) {
            if (positionOffset1 == 0) {
                positionOffset1 = positionOffsetPixels;
            } else if (positionOffset1 > 0) {
                if (positionOffsetPixels < 0 || positionOffsetPixels < positionOffset1) {
                    positionOffset2 = positionOffsetPixels;
                } else {
                    positionOffset1 = positionOffsetPixels;
                }
            } else {
                if (positionOffsetPixels > 0 || positionOffsetPixels > positionOffset1) {
                    positionOffset2 = positionOffsetPixels;
                } else {
                    positionOffset1 = positionOffsetPixels;
                }
            }
            float myPositionOffset = (float) positionOffsetPixels / (float) pageSlider.getWidth();
            if (positionOffset1 < 0) {
                if (positionOffset2 > 0) {
                    myPositionOffset = myPositionOffset - 1;
                }
            } else if (positionOffset1 > 0) {
                if (positionOffset2 < 0) {
                    myPositionOffset = 1 + myPositionOffset;
                }
            }
            if (positionOffset == 1 || positionOffset < 0.05) {
                positionOffset1 = 0;
                positionOffset2 = 0;
                position = pageSlider.getCurrentPage();
                myPositionOffset = 0;
            }

            this.currentItem = position;
            float tranlationX = (indicatorWidth + indicatorGap) * (position + myPositionOffset);
            selectView.setTranslationX(tranlationX);

        }
    }

    @Override
    public void onPageChosen(int position) {
        if (isSnap) {
            this.currentItem = position;
            for (int i = 0; i < indicatorViews.size(); i++) {
                indicatorViews.get(i).setImageElement(i == position ? selectDrawable : unSelectDrawable);
            }
            animSwitch(position);
            lastItem = position;
        }
    }

    @Override
    public void onPageSlideStateChanged(int state) {

    }

    private void animSwitch(int position) {
        try {
            if (selectAnimClass != null) {
                if (position == lastItem) {
                    getContext().getUITaskDispatcher().delayDispatch(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                selectAnimClass.newInstance().playOn(indicatorViews.get(position));
                            } catch (Exception e) {
                                Logger.getLogger(TAG).log(Level.SEVERE, e.getMessage());
                            }
                        }
                    }, 50);
                } else {
                    selectAnimClass.newInstance().playOn(indicatorViews.get(position));
                    if (unselectAnimClass == null) {
                        IndicatorBaseAnimator.reset(indicatorViews.get(lastItem));
                    } else {
                        unselectAnimClass.newInstance().playOn(indicatorViews.get(lastItem));
                    }
                }
            }
        } catch (Exception e) {
            Logger.getLogger(TAG).log(Level.SEVERE, e.getMessage());
        }
    }

    private void createIndicators() {
        if (count <= 0) {
            return;
        }

        indicatorViews.clear();
        rl_parent.removeAllComponents();

        DirectionalLayout llUnselectViews = new DirectionalLayout(context);
        llUnselectViews.setOrientation(HORIZONTAL);
        rl_parent.addComponent(llUnselectViews, new LayoutConfig(ComponentContainer.LayoutConfig.MATCH_CONTENT, ComponentContainer.LayoutConfig.MATCH_CONTENT));
        for (int i = 0; i < count; i++) {
            Image iv = new Image(context);
            // 给放大动画预留的空间
            iv.setPadding(indicatorWidth / 4, indicatorHeight / 4, indicatorWidth / 4, indicatorHeight / 4);
            iv.setImageElement(!isSnap ? unSelectDrawable : (currentItem == i ? selectDrawable : unSelectDrawable));
            LayoutConfig lp = new LayoutConfig(indicatorWidth, indicatorHeight);
            lp.setMarginLeft(i == 0 ? 0 : indicatorGap);
            llUnselectViews.addComponent(iv, lp);
            indicatorViews.add(iv);
        }

        if (!isSnap) {
            DependentLayout.LayoutConfig lp = new DependentLayout.LayoutConfig(indicatorWidth, indicatorHeight);
            selectView = new Image(context);
            selectView.setPadding(indicatorWidth / 4, indicatorHeight / 4, indicatorWidth / 4, indicatorHeight / 4);
            selectView.setImageElement(selectDrawable);
            rl_parent.addComponent(selectView, lp);
        }

        animSwitch(currentItem);
    }

    private boolean isValid() {
        if (pageSlider == null) {
            throw new IllegalStateException("PageSlider can not be NULL!");
        }

        if (pageSlider.getProvider() == null) {
            throw new IllegalStateException("PageSlider provider can not be NULL!");
        }

        return true;
    }

    private Element getDrawable(int color, float raduis) {
        ShapeElement drawable = new ShapeElement();
        drawable.setShape(ShapeElement.RECTANGLE);
        drawable.setCornerRadius(raduis);
        drawable.setStroke(strokeWidth, new RgbColor(strokeColor));
        drawable.setRgbColor(new RgbColor(color));
        return drawable;
    }


    private int vp2px(float vp) {
        return (int) (context.getResourceManager().getDeviceCapability().screenDensity / 160 * vp);
    }

}
